package com.istock.monitor.agent;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.istock.monitor.agent.config.PropertiesLoader;
import com.istock.monitor.agent.spring.MonitorAspectJExpressionPointCut;
import com.istock.monitor.agent.transformer.ClassRewriterRegistry;
import com.istock.monitor.agent.transformer.SpringInitialLoaderTransform;
import com.istock.monitor.agent.transformer.TransformerFactory;

public class Lanucher {

	private static Instrumentation instrumentation;
	public static void premain(String options , Instrumentation inst){
		System.out.println("this is premain");
		
		loadConfig(options);
		
		instrumentation = inst;
		ClassRewriterRegistry registry = new ClassRewriterRegistry();
		
		/*try{
			InputStream is = Lanucher.class.getClassLoader().getResourceAsStream("DataSourceBeanPostProcessor");
			ByteArrayOutputStream swapStream = new ByteArrayOutputStream();  
	        byte[] buff = new byte[100];  
	        int rc = 0;  
	        while ((rc = is.read(buff, 0, 100)) > 0) {  
	            swapStream.write(buff, 0, rc);  
	        }  
	        byte[] in2b = swapStream.toByteArray();
	        classList.add(in2b);
		}catch(Exception e){
			e.printStackTrace();
		}*/
		List<byte[]> classList = loadClassResource(new String[]{"META-INF/classes/DataSourceBeanPostProcessor","META-INF/classes/AutoAOPBeanFactoryPostProcessor"
				,"META-INF/classes/SpringInjector","META-INF/classes/MonitorAspectJExpressionPointCut","META-INF/classes/ClassNameAutoProxyCreator"});
		
		instrumentation.addTransformer(new TransformerFactory(registry));
		instrumentation.addTransformer(new SpringInitialLoaderTransform(classList));
	}
	
	private static List<byte[]> loadClassResource(String[] resources){
		List<byte[]> classList = new ArrayList<byte[]>();
		if(resources != null){
			for(String resource : resources){
				byte[] classBytes = loadClassByte(resource);
				if(classBytes != null) {
					classList.add(classBytes);
				}else{
					System.out.println("the resource "+resource+" not found");
				}
			}
		}
		return classList;
	}
	
	/** 把一个class,加载为byte,使用classloader进行手动加载
	 * @param resource
	 * @return
	 */
	private static byte[] loadClassByte(String resource) {
		try{
			InputStream is = Lanucher.class.getClassLoader().getResourceAsStream(resource);
			ByteArrayOutputStream swapStream = new ByteArrayOutputStream();  
	        byte[] buff = new byte[100];  
	        int rc = 0;  
	        while ((rc = is.read(buff, 0, 100)) > 0) {  
	            swapStream.write(buff, 0, rc);  
	        }  
	        return swapStream.toByteArray();
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}
	
	public static void loadConfig(String options){
		Map<String , String> optionMap = parseOptions(options);
		String configPath = "META-INF/config/conf.properties";
		if(optionMap.containsKey("configPath")){
			configPath = optionMap.get("configPath");
		}
		
		InputStream is = null;
		try{
			is = Lanucher.class.getClassLoader().getResourceAsStream(configPath);
		}catch(Exception e){
			System.out.println("can't load "+configPath+" from the class path");
		}
		
		if(is == null){
			try{
				is = new FileInputStream(new File(configPath));
			}catch(Exception e){
				System.out.println("can't load "+configPath+" from the file path");
			}
		}
		
		if(is != null){
			try{
				PropertiesLoader.getInstance().getProperties().load(is);
			}catch(Exception e){
				e.printStackTrace();
			}
		}	
	}
	
	private static Map<String , String> parseOptions(String options){
		Map<String ,String> paramMap = new HashMap<String , String>();
		if(options != null && options.length()>0){
			String[] args = StringUtils.split(options, ",");
			if(args != null && args.length>0){
				for(String arg : args){
					String[] item = StringUtils.split(arg, "=");
					if(item != null && item.length>0){
						if(item.length >= 2){
							paramMap.put(item[0], item[1]);
						}else{
							paramMap.put(item[0], "true");
						}
					}
				}
			}
		}
		return paramMap;
	}
}
